概述
本节完成购物车页面的基本列表样式开发。核心内容包括:基于 route.name 的条件渲染实现顶部 Tabs 动态切换,购物车条目的 Flex 布局设计,以及 TypeScript 接口定义与 Mock 数据绑定。
1. 顶部 Tabs 条件渲染
1.1 问题场景
学习模块(study)下包含列表页和购物车页,共享相同的头部结构。但购物车页面的顶部不应显示课程分类 Tabs,而应显示"购物车"标题。
1.2 基于 route.name 的条件判断
<!-- study.vue 头部 -->
<template>
<div class="header">
<!-- 购物车页面:显示"购物车"标题 -->
<div v-if="route.name === 'study-cart'" class="p-2 text-lg font-bold">
购物车
</div>
<!-- 其他页面:显示课程分类 Tabs -->
<div v-else>
<ul class="flex">
<li v-for="tab in tabs" :key="tab">{{ tab }}</li>
</ul>
</div>
<RouterView />
</div>
</template>
<script setup lang="ts">
const route = useRoute()
</script>
vue
1.3 route.name 与自动路由
使用 vite-plugin-pages 时,route.name 根据文件路径自动生成:
| 文件路径 | route.name |
|---|---|
pages/study/index.vue | study |
pages/study/cart.vue | study-cart |
注意拼写:cart(购物车)而非 card(卡片)。
2. 购物车条目布局
2.1 单条记录结构
每条购物车记录包含五个区域:复选框、头图、标题信息、价格、操作按钮。
<ul class="w-full">
<li
v-for="(course, index) in cartStore.courses"
:key="index"
class="flex items-center h-40 border-b"
>
<!-- 1. 复选框 -->
<div class="flex items-center justify-center px-4">
<input v-if="showCheckbox" type="checkbox" class="w-4 h-4" />
<span v-else class="iconify" data-icon="ep:check" />
</div>
<!-- 2. 课程头图 -->
<div class="py-2">
<div
class="w-20 h-32 bg-cover bg-center rounded"
:style="{ backgroundImage: `url(${course.image})` }"
/>
</div>
<!-- 3. 标题与信息 -->
<div class="flex-1 flex flex-col justify-between h-full py-2 pl-4">
<div>
<h3 class="font-bold text-lg">{{ course.title }}</h3>
</div>
<div>
<p class="text-sm text-gray-600">
{{ course.teacher }} | <span class="text-sky-400">{{ course.tag }}</span>
</p>
<p class="text-sm text-gray-400">
{{ course.count }}人已学习 · {{ course.progress }}
</p>
</div>
</div>
<!-- 4. 价格 -->
<div class="flex items-center justify-center h-full px-4">
<span class="text-lg font-bold text-sky-500">{{ course.price }}</span>
</div>
<!-- 5. 操作 -->
<div class="flex items-center justify-center h-full px-4">
<span class="text-red-500 cursor-pointer">删除</span>
</div>
</li>
</ul>
vue
2.2 布局要点
使用 h-40 固定条目高度,内部各区域使用 h-full 撑满:
┌──────┬──────────┬────────────────────┬──────────┬──────┐
│ 复选 │ 头图 │ 标题 │ 价格 │ 操作 │
│ 框 │ w-20 │ flex-1 │ │ │
│ px-4 │ h-32 │ flex-col │ px-4 │ px-4 │
│ │ bg-cover │ justify-between │ │ │
└──────┴──────────┴────────────────────┴──────────┴──────┘
text
垂直对齐:使用 flex items-center 使所有区域垂直居中。信息区域使用 justify-between 实现标题与元信息的分散对齐。
3. TypeScript 接口定义
3.1 数据模型
interface CartItem {
image: string // 课程头图 URL
title: string // 课程标题
teacher: string // 讲师名称
tag: string // 课程标签
count: number // 学习人数
progress: string // 更新进度
price: string // 价格
}
interface CartType {
courses: CartItem[]
}
ts
3.2 Mock 数据
import bg from '@/assets/images/bg.png'
const cartData = ref<CartType>({
courses: [
{
image: bg,
title: 'Vue3 + TypeScript 企业级实战',
teacher: '张老师',
tag: 'Vue3',
count: 2000,
progress: '已更新10-20',
price: '399.00',
},
],
})
ts
后续对接接口时,将 Mock 数据替换为 Store 或 API 请求返回的真实数据。
4. 样式调整细节
4.1 复选框显示逻辑
<input v-if="showCheckbox" type="checkbox" />
<span v-else class="iconify" data-icon="ep:check" />
vue
v-if / v-else 控制复选框的两种显示形态,具体交互逻辑在后续章节实现。
4.2 头图背景方式
使用 CSS background-image 而非 <img> 标签,便于精确控制裁剪和尺寸:
.course-thumb {
width: 5rem; /* w-20 */
height: 8rem; /* h-32 */
background-size: cover;
background-position: center;
border-radius: 0.25rem;
}
css
4.3 删除按钮
<div class="flex items-center justify-center h-full px-4">
<span class="text-red-500 cursor-pointer hover:text-red-700">删除</span>
</div>
vue
红色文字 + cursor-pointer 暗示可点击的删除操作。
5. 外层容器
<template>
<div class="my-4">
<div class="container">
<ul>
<!-- 购物车条目列表 -->
</ul>
</div>
</div>
</template>
vue
my-4:上下外边距container:限制最大宽度,水平居中
小结
| 要点 | 技术 |
|---|---|
| 顶部动态切换 | route.name + v-if/v-else |
| 条目布局 | Flex + 固定高度 h-40 + flex-1 |
| 数据定义 | TypeScript interface + defineProps |
| 头图显示 | background-image + bg-cover bg-center |
| 条件渲染 | v-if / v-else 切换复选框形态 |
↑